package socket;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Arrays;

/**
 * 聊天室服务端
 *
 */



public class Server {
    /**
     * java.net.ServerSocket
     * 运行在服务端的ServerSocket主要有两个作用:
     * 1:向系统申请固定的服务端口，客户端Socket就是通过这个端口进行连接的
     * 2:监听服务端口，一旦一个客户端连接就会立即返回一个Socket，通过它与客户端
     *   进行双向交互。
     *
     * 如果我们将Socket比喻为"电话",那么ServerSocket相当于"总机"
     */

    private ServerSocket serverSocket;
    /*
        该数组用来存放所有ClientHandler对应客户端的输出流，以便广播消息给
        所有客户端使用。
     */

    private PrintWriter[] allOut = {}; //为了扩容

    public Server(){
        try {



            System.out.println("正在启动服务端...");
            serverSocket = new ServerSocket(8088);
            System.out.println("服务端启动完毕！");

        } catch (IOException e) {
            e.printStackTrace();
        }


    }


    //开始工作的方法
    public void start(){
        //接电话
        try {
            while (true) {
                System.out.println("等待客户端连接...");
                Socket socket = serverSocket.accept();
                System.out.println("一个客户端连接了！");

                //启动一个线程来与该客户端交互
                ClientHandler handler = new ClientHandler(socket);
                Thread t = new Thread(handler);
                t.start();


            }
        } catch (IOException e) {
            e.printStackTrace();
        }



    }


    public static void main(String[] args) {
        Server server = new Server();
        server.start();
    }

    /**
     * 该线程任务用于与指定的客户端进行交互。
     *
     */

    private class ClientHandler implements Runnable{
        private Socket socket;
        private String host; //客户端的地址信息


        public ClientHandler(Socket socket){
            this.socket = socket;
            //通过socket获取远端计算机地址信息
            host = socket.getInetAddress().getHostAddress();
        }


        public void run(){
            PrintWriter pw = null;
            try{
                InputStream in = socket.getInputStream();

                InputStreamReader isr = new InputStreamReader(in);

                BufferedReader br = new BufferedReader(isr);

//                //通过socket获取
//                OutputStream out = socket.getOutputStream();

                pw = new PrintWriter(
                        new BufferedWriter(
                                new OutputStreamWriter(
                                        socket.getOutputStream()
                                        ,"UTF-8")
                        ),
                                true
                );
                //将该客户端的输出流存入共享数组


                synchronized (Server.this){
                    //1扩容
                    allOut = Arrays.copyOf(allOut,allOut.length+1);

                    //2将输出流存入数组最后一个位置
                    allOut[allOut.length-1] = pw;
                }


                sendMessage(host+"上线了,当前在线人数:"+allOut.length);

                //读取客户端发送的一行字符串
                String message;

            /*
                服务端通过缓冲流读取客户端发送过来一行字符串的操作时，这个方法
                会产生阻塞，等待对方发送消息，直到对方发送过来一行字符串则该方法
                返回此行内容。
                当客户端调用socket.close()断开连接，那么这里readLine方法会
                返回null，表述流读取到了末尾(对方断开了连接)
                如果客户端是意外中断(强行关闭客户端程序，停电，断网等)那么服务端
                这边readLine方法会抛出异常


             */

                while ((message = br.readLine()) != null) {

                    System.out.println(host+"说：" + message);
                    //将消息回复给客户端
                    sendMessage(host+"说："+message);

                }
            }catch (IOException e){
//                e.printStackTrace();

            }finally {
                //处理客户端断开连接后的操作

                synchronized (Server.this){
                    //将当前客户端的输出流从共享数组allOut中删除
                    for(int i=0;i<allOut.length;i++){
                        if(allOut[i]==pw){
                            allOut[i] = allOut[allOut.length-1];
                            allOut = Arrays.copyOf(allOut,allOut.length-1);
                            break;
                        }
                    }
                }

                sendMessage(host+"下线了,当前在线人数:"+allOut.length);

                try {
                    //与客户端断开连接释放资源

                    //将当前客户端的输出流从共享数组allOut中删除

                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }




        /**
         * 将给定的消息转发给所有客户端
         * @param message
         *
         */

        private void sendMessage(String message){
            synchronized (Server.this){
                for(int i=0;i<allOut.length;i++){
                    allOut[i].println(message);
                }

            }
        }
    }
}










